package com.moxaic.util;


import java.util.*;

/**
 * A generic empowered object
 *
 * @author Karl Ridgeway
 * @author John Magnotti IV
 *
 */
public abstract class Empowered implements Runnable {
    protected Thread controlThread;
    
    private Vector dataQueue;
    
    private Integer initializeSemaphore;
    
    protected boolean isRunning = true;
    
    private boolean hasStarted;
    
    public Empowered() {
        controlThread = new Thread(this, getThreadName());
        dataQueue= new Vector();
        hasStarted = false;
        
        initializeSemaphore = new Integer(0);
        controlThread.start();
        /**
         * There was a race condition between an external thread calling
         * addData() and performOp() before the empowered thread had
         * fully initialized. This caused the operation to never be performed.
         *
         * To fix it, we set a flag once the empowered thread reaches an acceptable state
         * and then return.
         */
        synchronized(initializeSemaphore) {
        while(!hasStarted) {
            try {
                initializeSemaphore.wait();
            } catch(InterruptedException ie) {
                ie.printStackTrace();
            }
        }
        }
    }
    
    
    public void shutDown() {
        isRunning = false;
        synchronized (this) {
            notifyAll();
        }
        try {
            controlThread.join();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }
        cleanup();
    }
    
    
    protected abstract void cleanup();
    
    
    public void run() {
        hasStarted = true;
        // Signal that we have started
        synchronized(initializeSemaphore) {
        	initializeSemaphore.notifyAll();
        }
        init();
        synchronized (this) {
            while (isRunning) {
                while (isRunning && dataQueue.size() == 0) {
                    try {
                        wait();
                    } catch (InterruptedException ie) {
                        ie.printStackTrace();
                    }
                }
                while (!dataQueue.isEmpty()) {
                    Object data = dataQueue.elementAt(dataQueue.size() -1);
                    dataQueue.removeElementAt(dataQueue.size()-1);
                    process(data);
                }
            }
        }
    }
    
    
    /**
     * Hook for implementing classes to do any setup work before the run loop is
     * engaged
     */
    protected abstract void init();
    
    
    protected abstract void process(Object data);
    
    
    protected String getThreadName() {
        return "Empowered";
    }
    
    
    protected void addData(Object data) {
        dataQueue.addElement(data);
    }
    
    
    public void performOp() {
        synchronized (this) {
            notifyAll();
        }
    }
}

